class EventBus {
  constructor() {
    this.events = Object.create(null);
  }

  on(event, cb) {
    if (typeof cb !== 'function') {
      throw new TypeError('Callback must be a function');
    }
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(cb);
    return this;
  }

  // 批量
  batch(events, cb) {
    events.forEach(event => {
      this.on(event, cb);
    });
    return this;
  }

  off(event, cb) {
    if (!cb) {
      delete this.events[event];
    } else {
      const listeners = this.events[event];
      if (listeners) {
        // 过滤掉当前回调及 once 的 origin 回调
        this.events[event] = listeners.filter(
          item => item !== cb && item.origin !== cb
        );
      }
    }
    return this;
  }

  clear() {
    this.events = Object.create(null);
    return this
  }

  listeners(event) {
    return this.events[event] || [];
  }

  has(event, cb) {
    const listeners = this.events[event];
    if (!listeners) return false;
    return listeners.some(item => item === cb || item.origin === cb);
  }

  once(event, cb) {
    if (typeof cb !== 'function') {
      throw new TypeError('Callback must be a function');
    }
    const wrapper = (...args) => {
      this.off(event, wrapper);
      cb.apply(this, args);
    };
    wrapper.origin = cb; // 保存原回调以便 off 识别
    this.on(event, wrapper);
    return this;
  }

  count(event) {
    return this.events[event] ? this.events[event].length : 0;
  }

  emit(event, ...args) {
    let listeners = this.events[event];
    if (!listeners || listeners.length === 0) {
      return this; // 静默处理未注册事件
    }
    listeners = [...listeners];
    listeners.forEach(cb => {
      try {
        cb.apply(this, args);
      } catch (err) {
        // 触发 error 事件处理异常
        const errorListeners = this.events.error;
        if (errorListeners && errorListeners.length > 0) {
          this.emit('error', err, event, args);
        } else {
          console.error(`Unhandled error in "${event}" listener:`, err);
        }
      }
    });
    return this;
  }
}

const eventBus = new EventBus();
export default eventBus;